package com.iot08.common.utils;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.*;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.HttpHostConnectException;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.springframework.web.util.UriUtils;

import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import java.io.*;
import java.net.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @version 1.0
 * @Description:
 * @time: 2020/9/1 9:48
 * @author: Rock
 */
@Slf4j
public class HttpReq {


    private static CloseableHttpClient httpClient;
    private static PoolingHttpClientConnectionManager poolingHttpClientConnectionManager;

    private static final int  MAX_CONN_TOTAL = 8000;
    private static final int  MAX_CONN_PERROUTE = 4000;

    static {
        poolingHttpClientConnectionManager = initPool();
    }

    public static CloseableHttpClient getHttpClient(){
        if(null != httpClient){
            return httpClient;
        }
        synchronized (HttpReq.class){
            if(null == httpClient){
                HttpRequestRetryHandler requestRetryHandler=new HttpRequestRetryHandler() {
                    public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
                        //TODO 超过重试次数，就放弃 这里如果太多次, 访问维智定位解析的那里会卡接口造成消息堆积问题
                        if (executionCount > 1)
                            return false;
                        if (exception instanceof NoHttpResponseException) {//没有响应，重试
                            return true;
                        }else if (exception instanceof HttpHostConnectException) {//连接异常，重试
                            log.error("连接异常HttpHostConnectException，重试 context:{}", context);
                            return true;
                        }else if (exception instanceof ConnectTimeoutException) {//连接超时，重试
                            log.error("连接超时ConnectTimeoutException，重试 context:{}", context);
                            return true;
                        } else if (exception instanceof SocketTimeoutException) {//连接或读取超时，重试
                            return true;
                        }else if (exception instanceof SSLHandshakeException) {//本地证书异常
                            return false;
                        } else if (exception instanceof InterruptedIOException) {//被中断
                            return false;
                        } else if (exception instanceof UnknownHostException) {//找不到服务器
                            return false;
                        }  else if (exception instanceof SSLException) {//SSL异常
                            return false;
                        } else {
                            log.error("未记录的请求异常：" + exception.getClass());
                        }
                        HttpClientContext clientContext = HttpClientContext.adapt(context);
                        HttpRequest request = clientContext.getRequest();
                        // 如果请求是幂等的，则重试
                        if (!(request instanceof HttpEntityEnclosingRequest)) return true;
                        return false;
                    }
                };
                httpClient = HttpClients.custom().setConnectionManager(poolingHttpClientConnectionManager).setRetryHandler(requestRetryHandler).build();
                return httpClient;
            }
        }
        return httpClient;
    }

    private static PoolingHttpClientConnectionManager initPool(){
        PoolingHttpClientConnectionManager pool = new PoolingHttpClientConnectionManager();
        // 最大连接数
        pool.setMaxTotal(MAX_CONN_TOTAL);
        // 每个路由基础的连接
        pool.setDefaultMaxPerRoute(MAX_CONN_PERROUTE);
        return pool;
    }

    /**
     * get请求
     *
     * @param url
     * @param authorization
     * @time: 2021/1/20 18:46
     * @author: Rock
     * @version 1.0
     */
    public static String sendGet(String url, String authorization) {
        CloseableHttpClient httpClient = getHttpClient();
        CloseableHttpResponse response = null;
        int statusCode = 0;
        HttpGet httpGet = new HttpGet();
        try {
            URI uri = new URI(url);
            httpGet.setURI(uri);
            RequestConfig config = RequestConfig.custom()
                    .setConnectionRequestTimeout(3000)
                    .setConnectTimeout(5000)
                    .setSocketTimeout(10000)
                    .setCookieSpec(CookieSpecs.IGNORE_COOKIES)
                    .build();
            httpGet.setConfig(config);
            if (StringUtil.isNotBlank(authorization)) {
                httpGet.addHeader("Authorization", authorization);
            }
            response = httpClient.execute(httpGet);
            //log.info("requets = {}. response = {} ", url, response);
            statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == HttpStatus.SC_OK) {
                String res = EntityUtils.toString(response.getEntity(), Consts.UTF_8);
                response.close();
                return res;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {

            httpGet.releaseConnection();
        }
        return null;
    }

    /**
     * @param url
     * @time: 2021/2/23 14:55
     * @author: Rock
     * @version 1.0
     */
    public static String sendGet(String url) {
        CloseableHttpClient httpClient = getHttpClient();
        CloseableHttpResponse response = null;
        int statusCode = 0;
        HttpGet httpGet = new HttpGet();
        try {
            URI uri = new URI(url);
            httpGet.setURI(uri);
            RequestConfig config = RequestConfig.custom()
                    .setConnectionRequestTimeout(3000)
                    .setConnectTimeout(5000)
                    .setSocketTimeout(10000)
                    .setCookieSpec(CookieSpecs.IGNORE_COOKIES)
                    .build();
            httpGet.setConfig(config);
            httpGet.setHeader("Content-type", "application/json");
            response = httpClient.execute(httpGet);
            statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == HttpStatus.SC_OK) {
                String res = EntityUtils.toString(response.getEntity(), Consts.UTF_8);
                response.close();
                return res;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            httpGet.releaseConnection();
        }
        return null;
    }

    /**
     * post请求
     *
     * @param url, json]
     * @time: 2020/9/1 9:55
     * @author: Rock
     * @version 1.0
     */
    public static String sendPostJson(String url, String json) {
        return sendPostJson(url,json,null);
    }

    /**
     * post请求
     *
     * @param url, json]
     * @time: 2020/9/1 9:55
     * @author: Rock
     * @version 1.0
     */
    public static String sendPostJson(String url, String json, Map<String, String> headers){
        long nowTime = System.currentTimeMillis();
        CloseableHttpClient httpClient = getHttpClient();
        CloseableHttpResponse response = null;
        int statusCode = 0;
        HttpPost post = new HttpPost();
        try {
            post.setURI(new URI(url));
            RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(5000).setConnectTimeout(5000)
                    .setSocketTimeout(10000).setCookieSpec(CookieSpecs.IGNORE_COOKIES).build();
            post.setConfig(config);
            if (json != null) {
                StringEntity requestEntity = new StringEntity(json, "utf-8");
                requestEntity.setContentEncoding("UTF-8");
                post.setEntity(requestEntity);
            }
            post.setHeader("Content-type", "application/json");
            if(headers != null){
                for (String headKey : headers.keySet()) {
                    String headValue = headers.get(headKey);
                    post.setHeader(headKey, headValue);
                }
            }
            response = httpClient.execute(post);
            statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == HttpStatus.SC_OK) {
                String result = EntityUtils.toString(response.getEntity(), Consts.UTF_8);
                //log.info("result = {}. param = {}", result, json);
                return result;
            } else {
                log.error("sendPostJson is fail. time = {}. url = {}. param = {}. response = {}", System.currentTimeMillis() - nowTime, url, json, response);
                return null;
            }
        } catch (Exception error) {
            log.error("sendPostJson is fail: "+error.getMessage(), error);
//            e.printStackTrace();
        } finally {
            post.releaseConnection();
        }
        return null;
    }

    /**
     * POST 接口上传字节流
     * @param url
     * @param data 字节流
     * @param headers
     * @return
     */
    public static String sendPostWithByteArr(String url, byte [] data, Map<String, String> headers){
        long nowTime = System.currentTimeMillis();
        CloseableHttpClient httpClient = getHttpClient();
        CloseableHttpResponse response = null;
        int statusCode = 0;
        HttpPost post = new HttpPost();
        try {
            post.setURI(new URI(url));
            RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(5000).setConnectTimeout(5000)
                    .setSocketTimeout(10000).setCookieSpec(CookieSpecs.IGNORE_COOKIES).build();
            post.setConfig(config);
            if (data != null) {
                post.setEntity(new ByteArrayEntity(data));
            }
            if(headers != null){
                for (String headKey : headers.keySet()) {
                    String headValue = headers.get(headKey);
                    post.setHeader(headKey, headValue);
                }
            }
            response = httpClient.execute(post);
            statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == HttpStatus.SC_OK) {
                String res = EntityUtils.toString(response.getEntity(), Consts.UTF_8);
                response.close();
                return res;
            } else {
                log.error("sendPostJson is fail. time = {}. url = {}. response = {}", System.currentTimeMillis() - nowTime, url, response);
                return null;
            }
        } catch (Exception error) {
            log.error("sendPostJson is fail: "+error.getMessage(), error);
//            e.printStackTrace();
        } finally {
            post.releaseConnection();
        }
        return null;
    }


    public static byte[] sendPostReturnInputStream(String url, String json, Map<String, String> headers){
        long nowTime = System.currentTimeMillis();
        CloseableHttpClient httpClient = getHttpClient();
        CloseableHttpResponse response = null;
        int statusCode = 0;
        HttpPost post = new HttpPost();
        try {
            post.setURI(new URI(url));
            RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(5000).setConnectTimeout(5000)
                    .setSocketTimeout(10000).setCookieSpec(CookieSpecs.IGNORE_COOKIES).build();
            post.setConfig(config);
            if (json != null) {
                StringEntity requestEntity = new StringEntity(json, "utf-8");
                requestEntity.setContentEncoding("UTF-8");
                post.setEntity(requestEntity);
            }
            post.setHeader("Content-type", "application/json");
            if(headers != null){
                for (String headKey : headers.keySet()) {
                    String headValue = headers.get(headKey);
                    post.setHeader(headKey, headValue);
                }
            }
            response = httpClient.execute(post);
            statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == HttpStatus.SC_OK) {
                byte[] bytes = readInputStream(response.getEntity().getContent());
                if (JSONObject.isValidObject(new String(bytes))) {
                    log.error("sendPostReturnInputStream 请求失败, {}", new String(bytes));
                    return null;
                }
                return bytes;
            } else {
                log.error("sendPostJson is fail. time = {}. url = {}. param = {}. response = {}", System.currentTimeMillis() - nowTime, url, json, response);
                return null;
            }
        } catch (Exception error) {
            log.error("sendPostJson is fail: "+error.getMessage(), error);
//            e.printStackTrace();
        } finally {
            post.releaseConnection();
        }
        return null;
    }

    /**  将流 保存为数据数组
     * @param inStream
     * @return
     * @throws Exception
     */
    public static byte[] readInputStream(InputStream inStream) throws Exception {
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        // 创建一个Buffer字符串
        byte[] buffer = new byte[1024];
        // 每次读取的字符串长度，如果为-1，代表全部读取完毕
        int len = 0;
        // 使用一个输入流从buffer里把数据读取出来
        while ((len = inStream.read(buffer)) != -1) {
            // 用输出流往buffer里写入数据，中间参数代表从哪个位置开始读，len代表读取的长度
            outStream.write(buffer, 0, len);
        }
        // 关闭输入流
        inStream.close();
        // 把outStream里的数据写入内存
        return outStream.toByteArray();
    }

    /**
     * 定位使用专用POST请求
     *
     * @param url
     * @param json
     * @time: 2021/6/8 14:03
     * @author: Rock
     * @version 1.0
     */
    public static String sendLBSLocatePostJson(String url, String json) {
        long nowTime = System.currentTimeMillis();
        CloseableHttpClient httpClient = getHttpClient();
        CloseableHttpResponse response = null;
        int statusCode = 0;
        HttpPost post = new HttpPost();
        try {
            post.setURI(new URI(url));
//            RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(2000).setConnectTimeout(2000)
//                    .setSocketTimeout(3000).setCookieSpec(CookieSpecs.IGNORE_COOKIES).build();
            RequestConfig config = RequestConfig.custom()
                    .setConnectionRequestTimeout(5000)
                    .setConnectTimeout(5000)
                    .setSocketTimeout(5000)
                    .setCookieSpec(CookieSpecs.IGNORE_COOKIES).build();
            post.setConfig(config);
            if (json != null) {
                StringEntity requestEntity = new StringEntity(json, "utf-8");
                requestEntity.setContentEncoding("UTF-8");
                post.setEntity(requestEntity);
            }
            post.setHeader("Content-type", "application/json");
            response = httpClient.execute(post);
            statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == HttpStatus.SC_OK) {
                String res = EntityUtils.toString(response.getEntity(), Consts.UTF_8);
                response.close();
                return res;
            } else {
                log.warn("sendWeiZhiPostJson is fail. time = {}. url = {}. param = {}. response = {}", System.currentTimeMillis() - nowTime, url, json, response);
                return null;
            }
        } catch (HttpHostConnectException e) {
            log.error(String.format("sendWeiZhiPostJson is timeout. time = %s. url = %s. param = %s. response = %s. e = %s", System.currentTimeMillis() - nowTime, url, json, response, e.getMessage()), e);
        } catch (Exception e) {
            log.error(String.format("sendWeiZhiPostJson is error time = %s. url = %s. param = %s. response = %s. e = %s", System.currentTimeMillis() - nowTime, url, json, response, e.getMessage()), e);
//            e.printStackTrace();
        }  finally {
            post.releaseConnection();
        }
        return null;
    }

    /**
     * 定位使用专用Get请求
     *
     * @param url
     * @time: 2021/6/8 14:03
     * @author: Rock
     * @version 1.0
     */
    public static String sendLBSLocateGet(String url) {
        long nowTime = System.currentTimeMillis();
        CloseableHttpClient httpClient = getHttpClient();
        CloseableHttpResponse response = null;
        int statusCode = 0;
        HttpGet httpGet = new HttpGet();
        try {
            URI uri = new URI(url);
            httpGet.setURI(uri);
            RequestConfig config = RequestConfig.custom()
                    .setConnectionRequestTimeout(5000)
                    .setConnectTimeout(5000)
                    .setSocketTimeout(5000)
                    .setCookieSpec(CookieSpecs.IGNORE_COOKIES)
                    .build();
            httpGet.setConfig(config);
            httpGet.setHeader("Content-type", "application/json");
            response = httpClient.execute(httpGet);
            statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == HttpStatus.SC_OK) {
                String res = EntityUtils.toString(response.getEntity(), Consts.UTF_8);
                response.close();
                return res;
            }
        } catch (Exception e) {
            log.error(String.format("sendGDGetJson is timeout. time = %s. url = %s. response = %s. e = %s", System.currentTimeMillis() - nowTime, url, response, e.getMessage()), e);
        } finally {
            httpGet.releaseConnection();
        }
        return null;
    }

    /**
     * 发送post请求, 附带Header 和请求参数
     * @param url
     * @param param
     * @param headers
     * @time: 5/27/22 2:00 PM
     * @author: Weichao
     * @version 1.0
     */
    public static String sendPostParam(String url, Map<String, String> param, Header[] headers) {
        CloseableHttpClient httpClient = getHttpClient();
        CloseableHttpResponse response = null;
        int statusCode = 0;
        HttpPost post = new HttpPost();
        try {
            post.setURI(new URI(url));
            RequestConfig config = RequestConfig.custom()
                    .setConnectionRequestTimeout(5000)
                    .setConnectTimeout(5000)
                    .setSocketTimeout(10000)
                    .setCookieSpec(CookieSpecs.IGNORE_COOKIES)
                    .build();
            post.setConfig(config);
            if (param != null && param.size() > 0) {
                List<NameValuePair> params = new ArrayList<>(param.size());
                for (Map.Entry<String, String> entry : param.entrySet()) {
                    params.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
                }
                UrlEncodedFormEntity requestEntity = new UrlEncodedFormEntity(params, "utf-8");
                requestEntity.setContentEncoding("UTF-8");
                post.setEntity(requestEntity);
            }
            if(headers != null){
                post.setHeaders(headers);
            }else{
                post.setHeader("Content-type", "application/x-www-form-urlencoded");
            }
            response = httpClient.execute(post);
            log.info(String.format("url:%s, param: %s, response: %s", url, param, response.toString()));
            statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == HttpStatus.SC_OK) {
                String result = EntityUtils.toString(response.getEntity(), Consts.UTF_8);
                log.info("result : " + result);
                response.close();
                return result;
            } else {
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            post.releaseConnection();
        }
        return null;
    }
    /**
     * @param url
     * @param param
     * @time: 2021/1/28 17:41
     * @author: Rock
     * @version 1.0
     */
    public static String sendPostParam(String url, Map<String, String> param) {
        return sendPostParam(url, param,null);
    }

    /**
     * 参数拼接
     *
     * @param parma
     * @time: 2021/1/20 18:55
     * @author: Rock
     * @version 1.0
     */
    public static String paramJoint(Map<String, Object> parma) {
        StringBuilder result = new StringBuilder();
        if (parma != null && parma.size() > 0) {
            for (Map.Entry<String, Object> entry : parma.entrySet()) {
                result.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
            }
            return result.substring(0, result.length() - 1);
        }
        return "";
    }


    /**
     * @description 百度地图
     * @author cwj
     * @param [strUrl, param]
     * @return com.alibaba.fastjson.JSONObject
     * @date @time 2024/10/14 16:08
     **/
    public static JSONObject sendBdLBSLocatePost(String strUrl,JSONObject param) throws IOException {
        if (strUrl == null || strUrl.length() <= 0 || param == null || param.size() <= 0) {
            return null;
        }
        java.net.URL url = new URL(strUrl);
        URLConnection httpConnection = (HttpURLConnection) url.openConnection();
        //启用输入输出流（允许写入和读取请求数据）
        httpConnection.setDoOutput(true);
        httpConnection.setDoInput(true);

        // 将数据写入请求体
        try (OutputStream os = httpConnection.getOutputStream()) {
            byte[] input = param.toString().getBytes("utf-8");
            os.write(input, 0, input.length);
        }
        InputStreamReader isr = new InputStreamReader(httpConnection.getInputStream());
        BufferedReader reader = new BufferedReader(isr);
        StringBuilder buffer = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            buffer.append(line);
        }
        reader.close();
        isr.close();
        JSONObject resultJson = JSONObject.parseObject(String.valueOf(buffer),JSONObject.class);
        //log.info("百度定位请求返回数据："+resultJson);
        return resultJson;
    }


    /**
     * @description 百度GET请求
     * @author cwj
     * @param [strUrl, param]
     * @return com.alibaba.fastjson.JSONObject
     * @date @time 2024/10/15 8:57
     **/
    public static JSONObject sendBdGet(String strUrl,Map<String,Object> param) {
        if (strUrl == null || strUrl.length() <= 0 || param == null || param.size() <= 0) {
            return null;
        }
        JSONObject resultJson = null;
        try{
            StringBuilder queryString = new StringBuilder();
            queryString.append(strUrl);
            for (Map.Entry<?, ?> pair : param.entrySet()) {
                queryString.append(pair.getKey()).append("=");
                if(pair.getValue() instanceof String){
                    queryString.append(UriUtils.encode((String) pair.getValue(), "UTF-8")).append("&");
                }else{
                    queryString.append(UriUtils.encode(pair.getValue()+"", "UTF-8")).append("&");
                }
            }

            if (queryString.length() > 0) {
                queryString.deleteCharAt(queryString.length() - 1);
            }

            java.net.URL url = new URL(queryString.toString());
            log.info("百度GET请求参数："+queryString);
            URLConnection httpConnection = (HttpURLConnection) url.openConnection();
            httpConnection.connect();

            InputStreamReader isr = new InputStreamReader(httpConnection.getInputStream());
            BufferedReader reader = new BufferedReader(isr);
            StringBuilder buffer = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                buffer.append(line);
            }
            reader.close();
            isr.close();
            resultJson = JSONObject.parseObject(String.valueOf(buffer),JSONObject.class);
        }catch (IOException ioe){
            log.error("百度GET请求发生IO异常，异常原因为：{}",ioe.getMessage());
            return resultJson;
        }catch (Exception e){
            log.error("百度GET请求发生异常，异常原因为：{}",e.getMessage());
            return resultJson;
        }
        //log.info("百度GET请求返回数据："+resultJson);
        return resultJson;
    }


    public static void main(String[] args) {
    }
}
